//#define USE_DUNGEONBUDDY_DLL

using System.Linq;
using CommonBehaviors.Actions;
using Styx.CommonBot;
using Styx.CommonBot.POI;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.TreeSharp;
using Styx;
using System.Collections.Generic;
using Action = Styx.TreeSharp.Action;

#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;

namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Classic
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
    namespace Bots.DungeonBuddy.Dungeon_Scripts.Classic
#endif
{
    public class Gnomeregan : Dungeon
    {
        #region Overrides of Dungeon

        /// <summary>
        ///   The mapid of this dungeon.
        /// </summary>
        /// <value> The map identifier. </value>
        public override uint DungeonId
        {
            get { return 14; }
        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(-5149, 904, 287.4014); }
        }

        public override WoWPoint ExitLocation
        {
            get { return new WoWPoint(-325.03, -5.06, -152.84); }
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (var unit in incomingunits)
            {
                if (unit.Entry == BombId && (!Me.IsMelee() || Me.IsMelee() && unit.ToUnit().CurrentTargetGuid != Me.Guid) &&
                    (!Me.IsTank() || _thermaplugg != null && _thermaplugg.IsValid && _thermaplugg.CurrentTargetGuid == Me.Guid))
                    outgoingunits.Add(unit);

                if (unit.Entry == MobileAlertSystemId && unit.Distance < 40 && (StyxWoW.Me.IsTank() || unit.Distance <= 25))
                    outgoingunits.Add(unit);
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var t in units)
            {
                var prioObject = t.Object;

                //Bombs on the last boss vital to living
                if (prioObject.Entry == BombId)
                {
                    t.Score += 1000;
                }
                //mobile alert systems, they spawn adds that wreck
                if (prioObject.Entry == MobileAlertSystemId)
                {
                    t.Score += 1000;
                }
            }
        }

        #endregion

        private const uint MobileAlertSystemId = 7849;
        private const uint CrowdPummeler960Id = 6229;
        private const uint BombId = 7915;
        private WoWUnit _thermaplugg;

        public WoWItem Item
        {
            get { return StyxWoW.Me.CarriedItems.FirstOrDefault(i => i.Entry == 60680); }
        }

        public WoWGameObject ParachutesBox
        {
            get { return (ObjectManager.GetObjectsOfType<WoWGameObject>().Where(u => u.Entry == 208325).OrderBy(u => u.Distance).FirstOrDefault()); }
        }

        private WoWUnit alert
        {
            get { return ObjectManager.GetObjectsOfType<WoWUnit>().Where(x => x.Entry == 7849 && x.IsAlive && x.Distance < 20).OrderBy(x => x.Distance).FirstOrDefault(); }
        }

        private WoWUnit bomb
        {
            get { return ObjectManager.GetObjectsOfType<WoWUnit>().Where(x => x.Entry == 7915 && x.IsAlive).OrderBy(x => x.Distance).FirstOrDefault(); }
        }

        private LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        WoWPoint _airplaneStuckLoc = new WoWPoint(-471.1491, 481.4396, -230.6018);

        [EncounterHandler(0, "Root")]
        public Composite Root()
        {
            AddAvoidLocation(ctx => _airplaneStuckLoc.Distance(Me.Location) < 20, 8f, o => _airplaneStuckLoc, null);

            return
                new PrioritySelector(
                    new Decorator(ctx => Me.IsFalling && Item != null && Me.Location.Z < -230.953 && Me.Location.Z > -260.953, new Action(ctx => Item.Use())),
                    new Decorator(ctx => Me.IsFalling && Item != null && Me.Location.Z < -170.953 && Me.Location.Z > -215.953, new Action(ctx => Item.Use())),
                //ScriptHelpers.CreateTankTalkToThenEscortNpc(7998, 0, new WoWPoint(-514.94, -138.54, -152.48), 7361), new WoWPoint(-514.94, -138.54, -152.48), 7361), 5, )

                    new Decorator(
                        ctx => ScriptHelpers.ShouldKillBoss("Grubbis") && ScriptHelpers.IsBossAlive("Grubbis"),
                        ScriptHelpers.CreateTankTalkToThenEscortNpc(
                            7998, 0, new WoWPoint(-514.94, -138.54, -152.48), new WoWPoint(-519.99, -124.85, -156.11), 6, () => true)));
        }

        [LocationHandler(-387.2501, 62.75173, -156.9798, 30, "Jump Location.")]
        public Composite ParachuteAreaBehavior()
        {
            var jumpStartLoc = new WoWPoint(-389.2496, 64.20731, -156.981);
            var jumpEndLoc = new WoWPoint(-436.232, 50.68904, -209.604);

            WoWGameObject box = null;
            return new PrioritySelector(
                ctx => box = ParachutesBox,
                // Get parachute from parachute box
                new Decorator(
                    ctx => box != null && Item == null && !StyxWoW.Me.IsActuallyInCombat && (!ScriptHelpers.WillPullAggroAtLocation(box.Location) || Me.IsTank()),
                    new PrioritySelector(
                        new Decorator(ctx => !box.WithinInteractRange, new Action(ctx => Navigator.MoveTo(box.Location))),
                        new Decorator(ctx => box.WithinInteractRange, new Sequence(new Action(ctx => WoWMovement.MoveStop()), new Action(ctx => box.Interact()))))),
                // jump off edge...
                ScriptHelpers.CreateJumpDown(ctx => Item != null && !StyxWoW.Me.IsActuallyInCombat, false, jumpStartLoc, jumpEndLoc));
        }

        [EncounterHandler(7079, "ViscousFallout")]
        public Composite ViscousFalloutFight()
        {
            return new PrioritySelector();
        }

        [EncounterHandler(6235, "Electrocutioner6000")]
        public Composite Electrocutioner6000Fight()
        {
            return new PrioritySelector();
        }

        [EncounterHandler(6229, "CrowdPummeler9-60")]
        public Composite CrowdPummeler960Fight()
        {
            WoWUnit boss = null;
            AddAvoidObject(ctx => Me.IsRange(), 11, u => u == boss && boss.CurrentTargetGuid != Me.Guid && boss.IsAlive);
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(ctx => !Me.IsTank(), () => boss, new ScriptHelpers.AngleSpan(0, 180)),
                ScriptHelpers.CreateTankFaceAwayGroupUnit(20));
        }

        [EncounterHandler(7800, "MekgineerThermaplugg")]
        public Composite MekgineerThermapluggFight()
        {
            WoWPoint centerLoc = new WoWPoint(-531.3243, 670.1588, -325.2682);

            var buttonIds = new uint[] { 142214, 142215, 142216, 142217, 142218, 142219 };
            var gnomeFaceIds = new uint[] { 142208, 142209, 142210, 142211, 142212, 142213 };

            AddAvoidObject(ctx => true, () => centerLoc, 35, 10, o => o.Entry == BombId && o.ToUnit().CharmedUnitGuid == Me.Guid);
            WoWGameObject button = null;
            return new PrioritySelector(
                ctx =>
                {
                    button = (from b in ObjectManager.GetObjectsOfType<WoWGameObject>().Where(o => buttonIds.Contains(o.Entry))
                              let myDist = Me.Location.Distance(b.Location)
                              where
                                  ObjectManager.GetObjectsOfType<WoWGameObject>().Where(o => gnomeFaceIds.Contains(o.Entry)).OrderBy(
                                      o => o.Location.DistanceSqr(b.Location)).FirstOrDefault().State == WoWGameObjectState.Active &&
                                  !Me.GroupInfo.RaidMembers.Where(p => p.HasRole(WoWPartyMember.GroupRole.Damage)).Select(p => p.ToPlayer()).Any(
                                      p => p != null && p.Location.Distance(b.Location) < myDist && p.IsSafelyFacing(b, 45))
                              orderby myDist
                              select b).FirstOrDefault();
                    return _thermaplugg = ctx as WoWUnit;
                },
                new Decorator(
                    ctx => button != null && Me.IsDps(),
                    new PrioritySelector(
                        new Decorator(ctx => button.WithinInteractRange, new Action(ctx => button.Interact())),
                        new Decorator(ctx => !button.WithinInteractRange, new Action(ctx => Navigator.MoveTo(button.Location))))));
        }
    }
}